-module(xengine_algo).
-include("xengine.hrl").
-include_lib("stdlib/include/qlc.hrl").
-export([connect/0]).
%%-compile(export_all).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%main function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
connect() ->
	AppNum  = length(configuration_manager:do(qlc:q([X || X<-mnesia:table(app)]))),
	MachineNum = length(configuration_manager:do(qlc:q([X || X<-mnesia:table(container)]))),
	case gen_tcp:connect("10.2.225.199",2345, [binary, {packet, 4}]) of
		{ok, Socket} ->
		    gen_tcp:send(Socket,get_str1(MachineNum, AppNum)),
		    parse_data(Socket, receive_data(Socket));
		{error, Reason} ->
			error_logger:info_msg("fail to connect algo_server....~n");
		Any ->
			error_logger:info_msg("~p~n",[Any])


	end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%internal  function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
receive_data(Socket)->
        receive
                {tcp,Socket,Bin} ->
			binary_to_list(Bin);
                {tcp_closed , Socket } ->
        		closed
	end.

parse_data(Socket, Response) ->
%%	io:format("algo received : ~p~n",[Response]),
	case string:tokens(Response, "\r\n") of
		[] ->
			[];
		[Line] ->
			case string:tokens(Line,"&") of
				[P1,P2] ->
					["msgtype",MsgType] = string:tokens(P1,"="),
					case MsgType of
						"1" ->
							["response",Rsp] = string:tokens(P2,"="),
							case Rsp of
								"ok" ->
									ok,
									parse_data(Socket, receive_data(Socket));
								Any ->
									error_logger:info_msg("response not ok.....wait")
							end;
						"2" ->
							["appid",AppId] = string:tokens(P2,"="),
							send_app_info(AppId, Socket),
							parse_data(Socket, receive_data(Socket))
					end;
				[P1, P2, P3] ->
					["msgtype", MsgType] = string:tokens(P1, "="),
					case MsgType of 
						"3" ->
						%%	["machinenum",MachineNum] = string:tokens(P2,"="),
						%%	["appnum", AppNum] = string:tokens(P3, "="),
							case gen_tcp:send(Socket, "msgtype=3&response=ok\r\n") of
								ok ->
									Apps = configuration_manager:do(qlc:q([X || X<-mnesia:table(app)])),
									lists:foreach(fun(App) ->
											Str1 = lists:append("msgtype=4&appname=",App#app.name),
											ReqMsg = lists:append(Str1, "&state=2\r\n"),
											case gen_tcp:send(Socket, ReqMsg) of
												ok ->
													parse_algo_data(Socket,receive_data(Socket))
											end
										  end, Apps),
									case gen_tcp:close(Socket) of
										ok ->
										%%	io:format("algo client closed~n"),
											ok;
										{error, Reason} ->
											error_logger:info_msg("fail to close Socket~n")
									end;
						%%			  timer:sleep(5000),
						%%	AppNum  = length(configuration_manager:do(qlc:q([X || X<-mnesia:table(app)]))),
						%%	MachineNum = length(configuration_manager:do(qlc:q([X || X<-mnesia:table(container)]))),
						%%			 case gen_tcp:send(Socket,get_str1(MachineNum, AppNum)) of
						%%				ok ->
						%%					parse_data(Socket, receive_data(Socket))
						%%				end;
								{error, Reason} ->
									error_logger:info_msg("fail to send msgtype 3.... ")
							end
					end
			end;
		Any ->		
			error_logger:msg_info("Any=~p~n",[Any]),
			[]
	end.
			
parse_algo_data(Socket, Response) ->
	case string:tokens(Response,"\r\n") of
		[] ->
			[];
		[Line] ->
			case string:tokens(Line, "%") of
				[P1, P2, P3] ->
					case string:tokens(P1, "&") of
						[S1, S2, S3] ->
							["msgtype", MsgType] = string:tokens(S1, "="),
							case MsgType of
								"4" ->
									["appname", AppName] = string:tokens(S2, "="),
									["machinenum", MachineNum] = string:tokens(S3, "=")
							end
					end,
					ContainerList = string:tokens(P2, "#"),
					Deployed_Container_Temp = configuration_manager:lookup_containers_by_app(AppName),
					Deployed_Container = lists:map(fun(Each_Deployed_Container)->
										Each_Deployed_Container#container.name
									end,Deployed_Container_Temp),
			 
					To_Undeploy_Container_List = lists:subtract(Deployed_Container,ContainerList),
					To_Deploy_Container_List = lists:subtract(ContainerList, Deployed_Container),
					lists:foreach(fun(To_Undeploy_Container) ->
								instance_manager:undeploy(AppName, To_Undeploy_Container)
							end, To_Undeploy_Container_List),
					lists:foreach(fun(To_Deploy_Container) ->
										instance_manager:deploy(AppName, To_Deploy_Container),
										timer:sleep(5000)
							end,To_Deploy_Container_List);
					
				[P1,P2] ->
					case string:tokens(P1,"&") of
						[S1,S2,S3] ->
							["msgtype",MsgType] = string:tokens(S1, "="),
							case MsgType of
								"4" ->
									["appname",AppName] = string:tokens(S2, "="),
									To_Undeploy_Container_Temp = configuration_manager:lookup_containers_by_app(AppName),
									To_Undeploy_Container_List = lists:map(fun(Each_Undeploy_Container) ->
														Each_Undeploy_Container#container.name
												end, To_Undeploy_Container_Temp),
									lists:foreach(fun(To_Undeploy_Container) ->
												instance_manager:undeploy(AppName,To_Undeploy_Container)
											end, To_Undeploy_Container_List),
									error_logger:info_msg("no container for this app...")
							end
					end
										
			end;
		Any ->
			error_logger:info_msg("received error algo data...")
	end.

	
send_app_info(AppId,Socket) ->
	Apps = configuration_manager:do(qlc:q([X || X<-mnesia:table(app)])),
	App = lists:nth(list_to_integer(AppId),Apps),
	HostedContainer = configuration_manager:lookup_containers_by_app(App#app.name),
	AllContainer = configuration_manager:do(qlc:q([X||X<-mnesia:table(container)])),
	NotHostedContainer = lists:subtract(AllContainer, HostedContainer),
	MachineNum = length(AllContainer),
	MsgHead = get_str2(AppId, MachineNum,App),
	MsgBody1 = append_hosted_container(HostedContainer,App),
	MsgBody2 = append_not_hosted_container(NotHostedContainer),
	MsgTail = "response=ok\r\n",
	Msg = lists:append(lists:append(MsgHead,MsgBody1), lists:append(MsgBody2, MsgTail)),
%%	io:format("algo send ~p~n",[Msg]),
	case gen_tcp:send(Socket, Msg) of
		ok ->
			ok;
		{error, Reason} ->
			error_logger:msg_info("~p~n",[Reason])
	end.	
		
			

				
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% the function deals with str %%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

get_str1(Machinenum, Appnum) ->
	Str1 = lists:append("msgtype=1&machinenum=",erlang:integer_to_list(Machinenum)),
	Str2 = lists:append(Str1, "&appnum="),
	Str3 = lists:append(Str2, erlang:integer_to_list(Appnum)),
	lists:append(Str3, "\r\n").
	

get_str2(AppId,MachineNum,App) ->
	Str1 = lists:append("msgtype=2&appid=",AppId),
	Str2 = lists:append(Str1, "&appname="),
	Str3 = lists:append(Str2, App#app.name),
	Str4 = lists:append(Str3, "&machinenum="),
	Str5 = lists:append(Str4, erlang:integer_to_list(MachineNum)),
	lists:append(Str5, "&").
	

append_hosted_container([Container|T],App) ->
	[ContainerRuntime] = configuration_manager:lookup_load_by_container(Container#container.name),
	[InstanceRuntime] = configuration_manager:lookup_load_by_instance(App#app.name, Container#container.name),
	error_logger:info_msg("algo ContainerRuntime:~p InstanceRuntime~p~n",[ContainerRuntime,InstanceRuntime]),
	ContainerCpuF = ContainerRuntime#runtime.cpu,
	CpuF = InstanceRuntime#runtime.cpu*ContainerCpuF,
	Cpu = trunc(100*CpuF),
	error_logger:info_msg("containercpu:~p,cpuf:~p,cpu~p~n",[ContainerCpuF,CpuF,Cpu]),
	Str1 = lists:append("machinename=", Container#container.name),
	Str2 = lists:append(Str1, "&state=2&load="),
	Str3 = lists:append(Str2, erlang:integer_to_list(Cpu)),
	Str = lists:append(Str3, "&"),
	lists:append(Str, append_hosted_container(T,App));
append_hosted_container([],AppId) -> [].
	
append_not_hosted_container([Container|T]) ->
	Str1 = lists:append("machinename=",Container#container.name),
	Str = lists:append(Str1, "&state=0&load=0&"),
	lists:append(Str, append_not_hosted_container(T));
append_not_hosted_container([]) -> [].

